//用以设计http request/response等相关协议,序列化和反序列化,从字节流中提取相关的信息
#pragma once
#include <iostream>
#include <cstdio>
#include <string>
#include <unordered_map>
#include <sstream>
#include "Logger.hpp"


const std::string line_sep = "\r\n";
const std::string status_sep = " ";
const std::string kv_sep = ": ";
const std::string webroot = "./source";
const std::string missing_path = "./source/text/404.html";//当访问页面不存在时，返回的404界面
const std::string default_path = "./source/text/page.html";//当单纯访问web根目录时，返回的界面


//请求方法，uri(?如果包含有问号，那么属于上传信息的部分),http版本，报头中的kv结构，正文内容，换行符分隔
class HttpRequest
{
    private:
    std::string ReadOneLine(std::string& request)
    {
        auto pos = request.find(line_sep);
        if(pos == std::string::npos)
            return std::string();
        std::string res = request.substr(0,pos);//获取这一行的内容(行分隔符之前的内容)
        request.erase(0,pos + line_sep.size());//将这一行连同分隔符一起删除
        return res;
    }

    //获取请求行相关内容
    bool ParseOneLine(const std::string& reqline)
    {
        if(reqline.empty())
            return false;
        std::stringstream ss(reqline);
        ss >> _method >> _uri >> _version;//从ss中读取字符串，以空格作为分隔符
        return true;
    }

    bool BuildKV(const std::string& line)
    {
        if(line.empty())
            return false;
        //划分出k,v
        auto pos = line.find(kv_sep);
        _kv.insert({line.substr(0,pos),line.substr(pos + kv_sep.size())});
        return true;
    }

    public:
    HttpRequest()
    {}

    //解析HttpRequset的最重要的工作：对无序字节流进行提取，即序列化和反序列化

    std::string Serialize(const HttpRequest& request)
    {
        //服务端仅作为接收方考虑
        return "";
    }

    //http请求的完整性检验(即请求行+报头部分)已经提前做好
    //服务端暂且只考虑GET方法,正文内容不存在
    bool DeSerialize(std::string& request)
    {
        if(request.empty())
            return false;
        std::string req_line = ReadOneLine(request);
        if(!ParseOneLine(req_line))
        {
            LOG(LogLevel::ERROR) << "ParseOneLine error";
            return false;
        }

        //循环获取KV结构
        while(true)
        {
            std::string line = ReadOneLine(request);
            if(line.empty())
                break;//此时已经读完了
            else
            {
                if(!BuildKV(line))
                {
                    LOG(LogLevel::ERROR) << "BuildKV error";
                    return false;
                }
            }
        }
        
        //此处先不处理包含?的情况
        auto pos = _uri.find('?');
        std::string path = _uri.substr(0,pos);
        if(path == "/")
            _path = default_path;
        else
            _path = webroot + path;
        return true;
        //对路径做相应处理
    }

    #define DEBUG
    #ifdef DEBUG
    void Print()
    {
        std::cout << _method << ' ' << _uri << ' ' << _version << std::endl;
        for(auto& e : _kv)
            std::cout << e.first << ':' << e.second << std::endl;
    }
    #endif
    //判断完整的标准：能不能找到"\r\n\r\n"
    static bool IsComplete(const std::string request)
    {
        return request.find("\r\n\r\n") != std::string::npos;
    }

    std::string GetVersion()
    {
        return _version;
    }

    std::string GetPath()
    {
        return _path;
    }
    ~HttpRequest()
    {}
    private:
    std::string _method;//GET,POST
    std::string _uri;//uri
    std::string _path;//请求资源的具体路径
    std::string _version;//http版本
    std::unordered_map<std::string,std::string> _kv;//存储报头kv结构
    std::string _body;//存储正文内容
};



//状态行：http版本/状态码/状态描述
//报头中的内容：Content-Type/Content-Length
//正文内容body
class HttpResponse
{
    public:
    HttpResponse()
    {}

    ~HttpResponse()
    {}

    //序列化
    bool Serialize(std::string& out)
    {
        std::string status = _version + status_sep + std::to_string(_code) + status_sep + _code_dsc + line_sep;
        out += status;

        for(auto& e : _kv)
        {
            std::string kv = e.first + kv_sep + e.second + line_sep;
            out += kv;
        }
        out += line_sep;
        
        out += _body;
        
        return true;
    }



    void CodeToDsc()
    {
        //做code到code描述的转换
        switch (_code)
        {
        case 200:
            _code_dsc = "OK";
            break;
        case 404:
            _code_dsc = "Not Found";
        case 301:
            _code_dsc = "Moved Permanently";
        case 302:
            _code_dsc = "See Other";
        default:
            break;
        }
    }

    std::string SuffixToType(const std::string& suffix)
    {
        if(suffix == ".html")
            return "text/html";//html文本
        else if(suffix == ".css")
            return "text/css";//css文本
        else if(suffix == ".js")
            return "text/js";//js文本
        else if(suffix == ".png")
            return "image/png";//图片
        else if(suffix == ".jpg")
            return "image/jpg";//图片
        return "";
    }

    void ReadContent(HttpRequest& request)
    {
        _path = request.GetPath();
        FILE* fp = fopen(_path.c_str(),"rb");
        if(fp == nullptr)
        {
            LOG(LogLevel::INFO) << "fopen error:404";
            fp = fopen(missing_path.c_str(),"rb");//打开404相应界面
            _path = missing_path;
        }
        fseek(fp,0,SEEK_END);
        int offset = ftell(fp);
        fseek(fp, 0, SEEK_SET);//将文件指针重置到开头:这一点太重要了
        _body.resize(offset);
        int n = fread(&_body[0],1,offset,fp);

        //for debug
        // std::cout << _path << std::endl;
        // std::cout << "读取到的字节数为:" << n << std::endl;
        // std::cout << "应该读取到的字节数为:" << offset << std::endl;
        
        if(n != offset)
        {
            LOG(LogLevel::ERROR) << "fread error";
        }
        fclose(fp);
    }
    void SetKV(const std::string& key,const std::string& val)
    {
        _kv.insert({key,val});
    }

    int GetBodySize()
    {
        return _body.size();
    }
    void SetPath(const std::string path)
    {
        _path = path;
    }

    std::string GetPath()
    {
        return _path;
    }

    void SetVersion(HttpRequest& request)
    {
        _version = request.GetVersion();
    }

    void SetCode(int code)
    {
        _code = code;
    }
    private:
    std::string _version;//http版本
    int _code;//状态码
    std::string _code_dsc;//状态码描述
    std::unordered_map<std::string,std::string> _kv;
    std::string _path;//对应的资源位置
    std::string _body; 
};